/*多路存储服务器
 日期：20150125
 */
#include "liveMedia.hh"
#include "GroupsockHelper.hh"
#include "BasicUsageEnvironment.hh"
#include <pthread.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h> 
#include <string.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <arpa/inet.h>
#include <iostream>
#include <libxml/parser.h>
#include <string.h>
#include <stdlib.h>  
#include <errno.h>  
#include <netdb.h>
#include <unistd.h> 
#include <strings.h>  
#include <unistd.h>  
#include <dirent.h>  
#include <sys/stat.h>  
#include <mysql/mysql.h>
#include <time.h>
#include "dbConnector.cpp"
using namespace std;


#define MAXDATASIZE 4096
#define SERVERPORT 8088
#define MAXCONN_NUM 10
#define MODE (S_IRWXU | S_IRWXG | S_IRWXO)  
#define ROOTDIR  "/video/"

char const* singleMedium = NULL;
const char * registeredmsg = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
                            <Envelope type=\"r_sregister\"> \
                                success \
                            </Envelope>";
pthread_t lpt;
char *registeredcenter="";
char buf[MAXDATASIZE]; 
int numbytes,new_fd,sockfd;
struct sockaddr_in server_addr; 
struct sockaddr_in client_addr; 

void *ListeningThread(void *arg);
int DecodeXml(char * buffer);

// 环境参数
char * progName="Lunax";
unsigned fileSinkBufferSize0 = 200000;
char eventLoopWatchVariable = 0;
Boolean outPutAviFile = false;
static unsigned rtspClientCount = 0; // 记录有多少RTSPClient正在使用
static int fileOutputIntervalset = 10; //输出文件时间间隔
#define RTSP_CLIENT_VERBOSITY_LEVEL 1 // 默认输出调试信息
#define REQUEST_STREAMING_OVER_TCP False //是否使用TCP连接
HashTable * fRtspClient = HashTable::create(0); //用来存储RTSPClient的Hash表
static int height = 480;
static int width = 640;
static char * filename_prefix="";
static char * filename_suffix="mp4";
static char * rtspuri="";
static char * deviceip="";
TaskScheduler* scheduler;
UsageEnvironment* env;
DbConnector *dbc;


//定义一个StreamClientState类来维护每个RTSPClient的生命周期
class StreamClientState {
public:
	StreamClientState();
	virtual ~StreamClientState();

public:
	MediaSubsessionIterator* iter;
	MediaSession* session;
	MediaSubsession* subsession;
	TaskToken streamTimerTask;
	double duration;
	bool flag;
};

//继承RTSPClient，定制自己的RTSPClient，实现多种方法
class ourRTSPClient: public RTSPClient {
public:
	static ourRTSPClient* createNew(UsageEnvironment& env, char const* rtspURL,
			int verbosityLevel = 0, char const* applicationName = NULL,
			portNumBits tunnelOverHTTPPortNum = 0);
	void createPeriodicOutputFiles0();
	void createOutputFiles0(char const * periodicFilenameSuffix);
	int createVideoFile();
	void closeMediaSinks0();
	void startStorage();
	void stopStorage();

protected:
	ourRTSPClient(UsageEnvironment& env, char const* rtspURL,
			int verbosityLevel, char const* applicationName,
			portNumBits tunnelOverHTTPPortNum);
	virtual ~ourRTSPClient();

public:
	StreamClientState scs;
	TaskToken periodicFileOutputTask0;
	unsigned fileOutputInterval; // 秒
	unsigned fileOutputSecondsSoFar; // 秒
	Medium* Out0;
	double initialSeekTime0;
	float scale0;
	double endTime0;
	char deviceip[20];
	char rtspurl[200];
	struct tm *starttime;
	char videoname[200];
};


// RTSP响应处理事件
void continueAfterDESCRIBE(RTSPClient* rtspClient, int resultCode, char* resultString);
void continueAfterSETUP(RTSPClient* rtspClient, int resultCode, char* resultString);
void continueAfterPLAY(RTSPClient* rtspClient, int resultCode, char* resultString);

// 其他事件句柄
void subsessionAfterPlaying(void* clientData); // 当子媒体流停止时调用
void subsessionByeHandler(void* clientData); // 当子媒体流接收到RTCP（BYE）时调用
void streamTimerHandler(void* clientData); // 处理流媒体结束时的持续时间（未接收到BYE命令）
void periodicFileOutputTimerHandler0(RTSPClient * rtspClient); //写文件事件
void sessionAfterPlaying0(void * rtspClient);

void openURL(UsageEnvironment& env, char const* progName, char const* rtspURL, char const* dip);

//用迭代器为每个子媒体会话建立连接
void setupNextSubsession(RTSPClient* rtspClient);

//关闭一个流媒体连接（包括RTSPClient对象）
void shutdownStream(RTSPClient* rtspClient, int exitCode = 1);

//根据rtspurl查找已存在的RTSPClient
ourRTSPClient * lookupClientByRTSPURL(const char * rtspurl);

UsageEnvironment& operator<<(UsageEnvironment& env,const RTSPClient& rtspClient);
UsageEnvironment& operator<<(UsageEnvironment& env,const MediaSubsession& subsession);
void usage(UsageEnvironment& env, char const* progName);
//创建目录
int MkDir(char *dir);

int main(int argc, char** argv) {
	scheduler = BasicTaskScheduler::createNew();
	env = BasicUsageEnvironment::createNew(*scheduler);
	dbc = DbConnector::createNew();
	/*
	if (argc < 2) {
		usage(*env, argv[0]);
		return 1;
	}
	for (int i = 1; i <= argc - 1; ++i) {
		openURL(*env, argv[0], argv[i]);
	}
	*/
	//const char *url1 = "rtsp://122.205.135.253:554/1/h264major";
	//const char *url1 = "rtsp://admin:12345@122.205.135.252:554/Streaming/Channels/1?transportmode=unicast&profile=Profile_1";
	//const char *url1 = "rtsp://admin:12345@122.205.135.252:554/cam/realmonitor?channel=1&subtype=0";
	//const char *url2 = "rtsp://122.205.135.254:554/1/h264major";
	//openURL(*env, argv[0], url1,"122.205.135.252");
	//openURL(*env, argv[0], url2,"122.205.135.254");

	//const char * tmp = ((RTSPClient *)fRtspClient->Lookup(url2))->url();
	//printf("###################%s\n",lookupClientByRTSPURL(url2)->url());
	//shutdownStream(lookupClientByRTSPURL(url2),1);
	int lthread = pthread_create(&lpt, NULL, ListeningThread, NULL);
	//printf("############%d\n",rtspClientCount);
	env->taskScheduler().doEventLoop(&eventLoopWatchVariable);
	return 0;
}


ourRTSPClient * lookupClientByRTSPURL(const char * rtspurl){
	return (ourRTSPClient *)fRtspClient->Lookup(rtspurl);
}

UsageEnvironment& operator<<(UsageEnvironment& env,const RTSPClient& rtspClient) {
	return env << "[URL:\"" << rtspClient.url() << "\"]: ";
}

UsageEnvironment& operator<<(UsageEnvironment& env,const MediaSubsession& subsession) {
	return env << subsession.mediumName() << "/" << subsession.codecName();
}

void usage(UsageEnvironment& env, char const* progName) {
	env << "Usage: " << progName << " <rtsp-url-1> ... <rtsp-url-N>\n";
	env << "\t(where each <rtsp-url-i> is a \"rtsp://\" URL)\n";
}

void openURL(UsageEnvironment& env, char const * progName, char const* rtspURL, char const* dip) {
	RTSPClient* rtspClient = ourRTSPClient::createNew(env, rtspURL,RTSP_CLIENT_VERBOSITY_LEVEL, progName);

	snprintf(((ourRTSPClient *)rtspClient)->deviceip, sizeof ((ourRTSPClient *)rtspClient)->deviceip, "%s", dip);
	snprintf(((ourRTSPClient *)rtspClient)->rtspurl, sizeof ((ourRTSPClient *)rtspClient)->rtspurl, "%s", rtspURL);
	time_t lt; 
	time(&lt);
	((ourRTSPClient *)rtspClient)->starttime = localtime(&lt);
	if (rtspClient == NULL) {
		env << "Failed to create a RTSP client for URL \"" << rtspURL << "\": "
				<< env.getResultMsg() << "\n";
		return;
	}
	//env << rtspClient->url() <<"\n";
	++rtspClientCount;
	//发送DESCRIBE命令，来获取SDP信息，这个命令并不会阻塞程序，响应事件在eventloop中处理
	rtspClient->sendDescribeCommand(continueAfterDESCRIBE);
}

//RTSP响应事件的实现
void continueAfterDESCRIBE(RTSPClient* rtspClient, int resultCode,char* resultString) {
	do {
		UsageEnvironment& env = rtspClient->envir(); // alias
		StreamClientState& scs = ((ourRTSPClient*) rtspClient)->scs; // alias

		if (resultCode != 0) {
			env << *rtspClient << "Failed to get a SDP description: "
					<< resultString << "\n";
			delete[] resultString;
			break;
		}
		char* const sdpDescription = resultString;
		env << *rtspClient << "Got a SDP description:\n" << sdpDescription<< "\n";
		//根据SDP信息创建一个会话对象
		scs.session = MediaSession::createNew(env, sdpDescription);
		delete[] sdpDescription;
		if (scs.session == NULL) {
			env << *rtspClient<< "Failed to create a MediaSession object from the SDP description: "<< env.getResultMsg() << "\n";
			break;
		} else if (!scs.session->hasSubsessions()) {
			env << *rtspClient<< "This session has no media subsessions (i.e., no \"m=\" lines)\n";
			break;
		}
		//为会话创建数据源并建立连接，在每个子会话上迭代操作
		//调用MediaSubsession::initiate()方法，然后为每个子会话发送SETUP命令，每个子会话拥有自己的数据源
		scs.iter = new MediaSubsessionIterator(*scs.session);
		setupNextSubsession(rtspClient);
		return;
	} while (0);

	//发生不可逆转的错误
	shutdownStream(rtspClient);
}

void setupNextSubsession(RTSPClient* rtspClient) {
	UsageEnvironment& env = rtspClient->envir(); // alias
	StreamClientState& scs = ((ourRTSPClient*) rtspClient)->scs; // alias

	scs.subsession = scs.iter->next();
	if (scs.subsession != NULL) {
		if (!scs.subsession->initiate()) {
			env << *rtspClient << "Failed to initiate the \"" << *scs.subsession<< "\" subsession: " << env.getResultMsg() << "\n";
			setupNextSubsession(rtspClient); //放弃这个子会话，进入下一个子会话
		} else {
			env << *rtspClient << "Initiated the \"" << *scs.subsession<< "\" subsession (";
			if (scs.subsession->rtcpIsMuxed()) {
				env << "client port " << scs.subsession->clientPortNum();
			} else {
				env << "client ports " << scs.subsession->clientPortNum() << "-"<< scs.subsession->clientPortNum() + 1;
			}
			env << ")\n";
			//继续为这个子会话建立连接，发送SETUP命令
			printf("Height : %d \t Width: %d \tFPS: %d\n",scs.subsession->videoHeight(),scs.subsession->videoWidth(),scs.subsession->videoFPS());

			rtspClient->sendSetupCommand(*scs.subsession, continueAfterSETUP,False, REQUEST_STREAMING_OVER_TCP);
		}
		return;
	}

	//((ourRTSPClient *) rtspClient)->createPeriodicOutputFiles0();  //文件输出
	((ourRTSPClient *) rtspClient)->createVideoFile();  //文件输出

	//为所有子会话建立连接成功，发送PLAY命令，开始传送流媒体
	if (scs.session->absStartTime() != NULL) {
		//流媒体使用绝对时间
		rtspClient->sendPlayCommand(*scs.session, continueAfterPLAY,scs.session->absStartTime(), scs.session->absEndTime());
	} else {
		//流媒体使用相对时间
		scs.duration = scs.session->playEndTime() - scs.session->playStartTime();
		rtspClient->sendPlayCommand(*scs.session, continueAfterPLAY);
	}
}

void continueAfterSETUP(RTSPClient* rtspClient, int resultCode,char* resultString) {
	do {
		UsageEnvironment& env = rtspClient->envir(); // alias
		StreamClientState& scs = ((ourRTSPClient*) rtspClient)->scs; // alias

		if (resultCode != 0) {
			env << *rtspClient << "Failed to set up the \"" << *scs.subsession<< "\" subsession: " << resultString << "\n";
			break;
		}
		env << *rtspClient << "Set up the \"" << *scs.subsession<< "\" subsession (";
		if (scs.subsession->rtcpIsMuxed()) {
			env << "client port " << scs.subsession->clientPortNum();
		} else {
			env << "client ports " << scs.subsession->clientPortNum() << "-"<< scs.subsession->clientPortNum() + 1;
		}
		env << ")\n";

		//为子会话建立连接成功，可以创建一个sink，然后调用startPlaying（）函数【这将创建一个sink准备接受数据，实际接受数据是在发送PLAY命令之后】
		/*暂时不用创建子回话sink
		//scs.subsession->sink = DummySink::createNew(env, *scs.subsession, rtspClient->url());

		if (scs.subsession->sink == NULL) {
			env << *rtspClient << "Failed to create a data sink for the \""
					<< *scs.subsession << "\" subsession: "
					<< env.getResultMsg() << "\n";
			break;
		}

		env << *rtspClient << "Created a data sink for the \"" << *scs.subsession << "\" subsession\n";
		scs.subsession->miscPtr = rtspClient; //为了让（subsession）子会话能够访问RTSPClient
		scs.subsession->sink->startPlaying(*(scs.subsession->readSource()),subsessionAfterPlaying, scs.subsession);
		//创建一个RTCP的BYE命令时间处理的句柄
		if (scs.subsession->rtcpInstance() != NULL) {
			scs.subsession->rtcpInstance()->setByeHandler(subsessionByeHandler,scs.subsession);
		}
		*/
	} while (0);
	delete[] resultString;
	//为下一个子会话建立连接（if any）
	setupNextSubsession(rtspClient);
}

void continueAfterPLAY(RTSPClient* rtspClient, int resultCode,char* resultString) {
	Boolean success = False;

	do {
		UsageEnvironment& env = rtspClient->envir(); // alias
		StreamClientState& scs = ((ourRTSPClient*) rtspClient)->scs; // alias

		if (resultCode != 0) {
			env << *rtspClient << "Failed to start playing session: "<< resultString << "\n";
			break;
		}

		//设置一个计时器，处理在流媒体结束时的持续时间
		// Set a timer to be handled at the end of the stream's expected duration (if the stream does not already signal its end
		// using a RTCP "BYE").  This is optional.  If, instead, you want to keep the stream active - e.g., so you can later
		// 'seek' back within it and do another RTSP "PLAY" - then you can omit this code.
		// (Alternatively, if you don't want to receive the entire stream, you could set this timer for some shorter value.)
		if (scs.duration > 0) {
			unsigned const delaySlop = 2; // number of seconds extra to delay, after the stream's expected duration.  (This is optional.)
			scs.duration += delaySlop;
			unsigned uSecsToDelay = (unsigned) (scs.duration * 1000000);
			scs.streamTimerTask = env.taskScheduler().scheduleDelayedTask(uSecsToDelay, (TaskFunc*) streamTimerHandler, rtspClient);
		}

		env << *rtspClient << "Started playing session";
		if (scs.duration > 0) {
			env << " (for up to " << scs.duration << " seconds)";
		}
		env << "...\n";

		success = True;
	} while (0);
	delete[] resultString;

	if (!success) {
		//发生不可逆转的错误
		shutdownStream(rtspClient);
	}
}

void subsessionAfterPlaying(void* clientData) {
	MediaSubsession* subsession = (MediaSubsession*) clientData;
	RTSPClient* rtspClient = (RTSPClient*) (subsession->miscPtr);

	//开始关闭子会话
	Medium::close(subsession->sink);
	subsession->sink = NULL;

	//检查是否所有子会话已关闭
	MediaSession& session = subsession->parentSession();
	MediaSubsessionIterator iter(session);
	while ((subsession = iter.next()) != NULL) {
		if (subsession->sink != NULL)
			return; // 这个子会话还未关闭
	}

	//所有子会话已关闭，然后停止RTSPClient
	shutdownStream(rtspClient);
}

void subsessionByeHandler(void* clientData) {
	MediaSubsession* subsession = (MediaSubsession*) clientData;
	RTSPClient* rtspClient = (RTSPClient*) subsession->miscPtr;
	UsageEnvironment& env = rtspClient->envir(); // alias

	env << *rtspClient << "Received RTCP \"BYE\" on \"" << *subsession<< "\" subsession\n";

	//当做子会话已关闭来处理
	subsessionAfterPlaying(subsession);
}

void streamTimerHandler(void* clientData) {
	ourRTSPClient* rtspClient = (ourRTSPClient*) clientData;
	StreamClientState& scs = rtspClient->scs; // alias
	scs.streamTimerTask = NULL;
	//关闭流媒体传输
	shutdownStream(rtspClient);
}

void shutdownStream(RTSPClient* rtspClient, int exitCode) {
	UsageEnvironment& env = rtspClient->envir(); // alias
	StreamClientState& scs = ((ourRTSPClient*) rtspClient)->scs; // alias

	//首先检查是否还存在子会话需要关闭
	if (scs.session != NULL) {
		Boolean someSubsessionsWereActive = False;
		MediaSubsessionIterator iter(*scs.session);
		MediaSubsession* subsession;

		while ((subsession = iter.next()) != NULL) {
			if (subsession->sink != NULL) {
				Medium::close(subsession->sink);
				subsession->sink = NULL;
				if (subsession->rtcpInstance() != NULL) {
					subsession->rtcpInstance()->setByeHandler(NULL, NULL); // 防止在处理TEARDOMN命令时服务器发送BYE命令
				}
				someSubsessionsWereActive = True;
			}
		}
		if (someSubsessionsWereActive) {
			//发送TEARDOWN命令，告诉服务器关闭流媒体传输，这个不会影响对TEARDOWN命令的处理事件
			rtspClient->sendTeardownCommand(*scs.session, NULL);
		}
	}

	env << *rtspClient << "Closing the stream.\n";
	Medium::close(rtspClient);
	//这个地方也会释放StreamClientState

	if (--rtspClientCount == 0) {
		//当没有连接时退出程序，或者用eventLoopWatchVariable = 1来替换，控制eventloop是否继续。
		//eventLoopWatchVariable = 1;
		//exit(exitCode);
	}
}

ourRTSPClient* ourRTSPClient::createNew(UsageEnvironment& env,
		char const* rtspURL, int verbosityLevel, char const* applicationName,
		portNumBits tunnelOverHTTPPortNum) {
	return  new ourRTSPClient(env, rtspURL, verbosityLevel, applicationName,
			tunnelOverHTTPPortNum);
}


void ourRTSPClient::createPeriodicOutputFiles0() {
	char periodicFileNameSuffix[100];
	snprintf(periodicFileNameSuffix, sizeof periodicFileNameSuffix,
			"-%05d-%05d", fileOutputSecondsSoFar,
			fileOutputSecondsSoFar + fileOutputInterval);
	this->createOutputFiles0(periodicFileNameSuffix);

	periodicFileOutputTask0 = envir().taskScheduler().scheduleDelayedTask(
			fileOutputInterval * 1000000,
			(TaskFunc*) periodicFileOutputTimerHandler0, this);
}

void ourRTSPClient::createOutputFiles0(char const * periodicFileNameSuffix) {
	char outFileName[1000];
	//char const* prefix = this->name();
	snprintf(outFileName, sizeof outFileName, "%s%s.%s", filename_prefix,periodicFileNameSuffix, outPutAviFile?"avi":"mp4");

	if(strcmp(filename_suffix,"mp4")==0){
		Out0 = QuickTimeFileSink::createNew(envir(), *scs.session, outFileName,fileSinkBufferSize0, width, height, 15, false, false, false, true);
		if (Out0 == NULL) {
			envir()<< "Failed to create a \"QuickTimeFileSink\" for outputting to \""
				<< outFileName << "\": " << envir().getResultMsg() << "\n";
			closeMediaSinks0();
		} else {
			envir() << "Outputting to the file: \"" << outFileName << "\"\n";
		}
		((QuickTimeFileSink *)Out0)->startPlaying(sessionAfterPlaying0, this);
	}
	if(strcmp(filename_suffix,"avi")==0){
		Out0 = AVIFileSink::createNew(envir(), *scs.session, outFileName,fileSinkBufferSize0, width, height, 25, false);
		if (Out0 == NULL) {
			envir()<< "Failed to create a \"AVIFileSink\" for outputting to \""
				<< outFileName << "\": " << envir().getResultMsg() << "\n";
			closeMediaSinks0();
		} else {
			envir() << "Outputting to the file: \"" << outFileName << "\"\n";
		}
		((AVIFileSink *)Out0)->startPlaying(sessionAfterPlaying0, this);
	}
}

void periodicFileOutputTimerHandler0(RTSPClient *rtspClient) {
	((ourRTSPClient*) rtspClient)->fileOutputSecondsSoFar +=
			((ourRTSPClient*) rtspClient)->fileOutputInterval;
	((ourRTSPClient*) rtspClient)->closeMediaSinks0();
	if(((ourRTSPClient *) rtspClient)->scs.flag){
		((ourRTSPClient*) rtspClient)->createPeriodicOutputFiles0();
	}else{
		shutdownStream((ourRTSPClient *) rtspClient, 1);
	}
}

void sessionAfterPlaying0(void *rtspClient) {
	if (&(((ourRTSPClient *) rtspClient)->envir()) != NULL) {
		((ourRTSPClient *) rtspClient)->envir().taskScheduler().unscheduleDelayedTask(
				((ourRTSPClient*) rtspClient)->periodicFileOutputTask0);
		((ourRTSPClient *) rtspClient)->envir().taskScheduler().unscheduleDelayedTask(
				((ourRTSPClient*) rtspClient)->scs.streamTimerTask);
	}
	((ourRTSPClient *) rtspClient)->sendPlayCommand(
			*(((ourRTSPClient*) rtspClient)->scs.session), continueAfterPLAY,
			((ourRTSPClient*) rtspClient)->initialSeekTime0,
			((ourRTSPClient*) rtspClient)->endTime0,
			((ourRTSPClient*) rtspClient)->scale0, NULL);
}

void ourRTSPClient::closeMediaSinks0() {
	Medium::close(this->Out0);
	Out0 = NULL;
	if (scs.session == NULL)
		return;
	MediaSubsessionIterator iter(*(scs.session));
	MediaSubsession* subsession;
	while ((subsession = iter.next()) != NULL) {
		Medium::close(subsession->sink);
		subsession->sink = NULL;
	}
}


int ourRTSPClient::createVideoFile(){
	if(chdir(ROOTDIR)==-1){
        fprintf(stderr,"改变当前工作目录失败");
        return -1;
    }
	struct tm *newtime;
	char tmpbuf[128];
	time_t lt1; 
	time(&lt1);
	newtime=localtime(&lt1);
	strftime(tmpbuf,128,"%F",newtime);
	if(MkDir(this->deviceip)==-1){
        fprintf(stderr,"创建目录失败");
        return -1;
    }
	if(chdir(this->deviceip)==-1){
        fprintf(stderr,"改变当前工作目录失败");
        return -1;
    }
	if(MkDir(tmpbuf)==-1){
        fprintf(stderr,"创建目录失败");
        return -1;
    }
	if(chdir(tmpbuf)==-1){
        fprintf(stderr,"改变当前工作目录失败");
        return -1;
    }
	strftime(tmpbuf,128,"%F-%T",newtime);
	//snprintf(this->videoname, sizeof this->videoname, "%s.%s",tmpbuf, outPutAviFile?"avi":"mp4");

	if(strcmp(filename_suffix,"mp4")==0){
		snprintf(this->videoname, sizeof this->videoname, "%s.%s",tmpbuf, "mp4");
		Out0 = QuickTimeFileSink::createNew(envir(), *scs.session, this->videoname,fileSinkBufferSize0, width, height, 15, false, false, false, true);
		if (Out0 == NULL) {
			envir()<< "Failed to create a \"QuickTimeFileSink\" for outputting to \""
				<< this->videoname << "\": " << envir().getResultMsg() << "\n";
			closeMediaSinks0();
		} else {
			envir() << "Outputting to the file: \"" << this->videoname << "\"\n";
		}
		((QuickTimeFileSink *)Out0)->startPlaying(sessionAfterPlaying0, this);
	}
	if(strcmp(filename_suffix,"avi")==0){
		snprintf(this->videoname, sizeof this->videoname, "%s.%s",tmpbuf, "avi");
		Out0 = AVIFileSink::createNew(envir(), *scs.session, this->videoname,fileSinkBufferSize0, width, height, 25, false);
		if (Out0 == NULL) {
			envir()<< "Failed to create a \"AVIFileSink\" for outputting to \""
				<< this->videoname << "\": " << envir().getResultMsg() << "\n";
			closeMediaSinks0();
		} else {
			envir() << "Outputting to the file: \"" << this->videoname << "\"\n";
		}
		((AVIFileSink *)Out0)->startPlaying(sessionAfterPlaying0, this);
	}
	if (strcmp(filename_suffix, "H264") == 0) {

		scs.iter =  new MediaSubsessionIterator(*scs.session);
		FileSink* fs;
    	while ((scs.subsession = scs.iter->next()) != NULL) {
      		if (scs.subsession->readSource() == NULL) continue;

     		Boolean createOggFileSink = False; // by default
      		if (strcmp(scs.subsession->mediumName(), "video") == 0) {
				if (strcmp(scs.subsession->codecName(), "H264") == 0) {
					snprintf(this->videoname, sizeof this->videoname, "%s.%s",tmpbuf, "264");
	  				fs = H264VideoFileSink::createNew(*env, this->videoname,scs.subsession->fmtp_spropparametersets(),fileSinkBufferSize0, false);
				}else if(strcmp(scs.subsession->codecName(), "H265") == 0) {
					snprintf(this->videoname, sizeof this->videoname, "%s.%s",tmpbuf, "265");
	  				fs = H265VideoFileSink::createNew(*env, this->videoname,scs.subsession->fmtp_spropvps(),scs.subsession->fmtp_spropsps(),scs.subsession->fmtp_sproppps(),fileSinkBufferSize0, false);
				}else if(strcmp(scs.subsession->codecName(), "THEORA") == 0) {
	  				createOggFileSink = True;
				}
      		}

      		else if(strcmp(scs.subsession->mediumName(), "audio") == 0) {
				if (strcmp(scs.subsession->codecName(), "AMR") == 0 || strcmp(scs.subsession->codecName(), "AMR-WB") == 0) {
					snprintf(this->videoname, sizeof this->videoname, "%s.%s",tmpbuf, "amr");
	  				fs = AMRAudioFileSink::createNew(*env, this->videoname,fileSinkBufferSize0, false);
				}else if(strcmp(scs.subsession->codecName(), "VORBIS") == 0 || strcmp(scs.subsession->codecName(), "OPUS") == 0) {
	  				createOggFileSink = True;
				}
      		}
      		if (createOggFileSink) {
      			snprintf(this->videoname, sizeof this->videoname, "%s.%s",tmpbuf, "ogg");
				fs = OggFileSink::createNew(*env, this->videoname,scs.subsession->rtpTimestampFrequency(), scs.subsession->fmtp_config());
      		}else if(fs == NULL) {
      			snprintf(this->videoname, sizeof this->videoname, "%s.%s",tmpbuf, "unknown");
				fs = FileSink::createNew(*env, this->videoname,fileSinkBufferSize0, false);
      		}
      		scs.subsession->sink = fs;
      		if (scs.subsession->sink == NULL) {
				*env << "Failed to create FileSink for \"" << this->videoname<< "\": " << env->getResultMsg() << "\n";
      		}else{
				if (singleMedium == NULL) {
	  				*env << "Created output file: \"" << this->videoname << "\"\n";
				}else{
	  				*env << "Outputting data from the \"" << scs.subsession->mediumName()<< "/" << scs.subsession->codecName()
	       				<< "\" subsession to \"" << this->videoname << "\"\n";
				}
				if (strcmp(scs.subsession->mediumName(), "video") == 0 && strcmp(scs.subsession->codecName(), "MP4V-ES") == 0 &&
	    				scs.subsession->fmtp_config() != NULL) {
	  				unsigned configLen;
	  				unsigned char* configData = parseGeneralConfigStr(scs.subsession->fmtp_config(), configLen);
					struct timeval timeNow;
					gettimeofday(&timeNow, NULL);
					fs->addData(configData, configLen, timeNow);
					delete[] configData;
				}
				scs.subsession->sink->startPlaying(*(scs.subsession->readSource()),subsessionAfterPlaying,scs.subsession);
				if (scs.subsession->rtcpInstance() != NULL) {
  					scs.subsession->rtcpInstance()->setByeHandler(subsessionByeHandler, scs.subsession);
				}
    		}
		}
	}
}

void ourRTSPClient::startStorage(){
	this->createVideoFile();
}

void ourRTSPClient::stopStorage(){
	this->closeMediaSinks0();
	shutdownStream(this, 1);
	char path[200],sqlbuf[500],cmd[200];   
	char datebuf[50],timebuf[50];
	strftime(datebuf,50,"%F",this->starttime);
	strftime(timebuf,50,"%X",this->starttime); 

   struct tm *endtime;
	char endtimebuf[128];
	time_t lt; 
	time(&lt);
	endtime=localtime(&lt);
	strftime(endtimebuf,128,"%X",endtime);

  	struct sockaddr_in ourAddress;
   ourAddress.sin_addr.s_addr = ourIPAddress(envir());
	memset(path,0,strlen(path));
	printf("rtspurl------------------:%s\n",path);
   strcat(path,"rtsp://");
   strcat(path, AddressString(ourAddress).val());
	strcat(path,":8554/");
	strcat(path,this->deviceip);
	strcat(path,"/");
	strcat(path,datebuf);
	strcat(path,"/");
	strcat(path,this->videoname);
	printf("rtspurl------------------:%s\n",path);
	char *sql="insert into videoindex (deviceip,rtspuri,sdate,starttime,endtime,desturi)values('%s','%s','%s','%s','%s','%s');";	
	sprintf(sqlbuf,sql,this->deviceip,this->rtspurl,datebuf,timebuf,endtimebuf,path);
	printf("%s\n",sqlbuf);
	if(dbc->SQLExecute(sqlbuf)){
		printf("close success.....\n");
	}
	//为文件添加hint track
	memset(cmd,0,strlen(cmd));
	strcat(cmd,"mp4creator -hint=1 /video/");
	strcat(cmd,this->deviceip);
	strcat(cmd,"/");
	strcat(cmd,datebuf);
	strcat(cmd,"/");
	strcat(cmd,this->videoname);
	printf("cmd-------------------:%s\n",cmd);
	if(system(cmd) == -1){
		perror("fail to exec command...\n");
	}
}

ourRTSPClient::ourRTSPClient(UsageEnvironment& env, char const* rtspURL,
		int verbosityLevel, char const* applicationName,
		portNumBits tunnelOverHTTPPortNum) :
		RTSPClient(env, rtspURL, verbosityLevel, applicationName,
				tunnelOverHTTPPortNum, -1) {
	fileOutputInterval = fileOutputIntervalset; // 秒
	fileOutputSecondsSoFar = 0; // 秒
	Out0 = NULL;
	initialSeekTime0 = 0.0f;
	scale0 = 1.0f;
	endTime0 = 0.0f;
	periodicFileOutputTask0 = NULL;
	fRtspClient->Add(this->url(),(void *)this);
}

ourRTSPClient::~ourRTSPClient() {
	fRtspClient->Remove(this->url());
}

StreamClientState::StreamClientState() :
		iter(NULL), session(NULL), subsession(NULL), streamTimerTask(NULL), duration(
				0.0),flag(true) {
}

StreamClientState::~StreamClientState() {
	delete iter;
	if (session != NULL) {
		//删除session并清空streamTimerTask
		UsageEnvironment& env = session->envir(); // alias
		env.taskScheduler().unscheduleDelayedTask(streamTimerTask);
		Medium::close(session);
	}
}


void *ListeningThread(void *arg)
{
    socklen_t sin_size; 
    if ((sockfd = socket ( AF_INET , SOCK_STREAM , 0)) == - 1) { 
        perror ("socket error"); 
        pthread_exit((void *)-1);
    } 
    memset(&client_addr,0,sizeof(struct sockaddr)); 
    server_addr.sin_family = AF_INET; 
    server_addr.sin_port = htons(SERVERPORT); 
    server_addr.sin_addr.s_addr = INADDR_ANY; 
    if ( bind ( sockfd, (struct sockaddr *) & server_addr, sizeof (struct sockaddr)) == -1) {
        perror ("bind error"); 
        pthread_exit((void *)-1);
    } 
    if (listen(sockfd, MAXCONN_NUM) ==-1) { 
        perror("listen error"); 
        pthread_exit((void *)-1);
    } 

    while (1) {
        sin_size = sizeof ( struct sockaddr_in ); 
        if ( ( new_fd = accept ( sockfd, (struct sockaddr *)&client_addr, &sin_size) ) == - 1) { 
            perror ( "accept error" ); 
            continue ; 
        } 
        printf ( "storageserver: got connection from %s\n" , inet_ntoa( client_addr. sin_addr) ); 
        while (1) {
            if ( ( numbytes = recv ( new_fd, buf, MAXDATASIZE, 0) ) == - 1) {
                perror ( "recv error" );
                pthread_exit((void *)-1);
            }
            buf[numbytes] = '\0';
            if(strlen(buf) <= 0){
                break;
            }
           // printf("received:%s\n",buf);
            DecodeXml(buf);

        }
        printf("Server: disconnect from %s\n",inet_ntoa( client_addr. sin_addr) );
        close ( new_fd); 
    } 
}

int DecodeXml(char * buffer){
    xmlDocPtr doc = xmlParseMemory(buffer,strlen(buffer));
    if (doc == NULL){
        return -1;
    }
    xmlNodePtr curNode = xmlDocGetRootElement(doc); //get root element
    if (curNode == NULL){
        xmlFreeDoc(doc);
        return -2;
    }
    if (xmlStrcmp(curNode->name, BAD_CAST "Envelope")){  //匹配Envelope
        xmlFreeDoc(doc);
        return -3;
    }
    if (xmlHasProp(curNode,BAD_CAST "type")){
        xmlChar * szAttr = xmlGetProp(curNode,BAD_CAST "type");
        cout <<"XMLDecoded:" << szAttr << endl;

        if(!xmlStrcmp(szAttr,BAD_CAST "sregister")){  //匹配cmsregister
            registeredcenter = inet_ntoa( client_addr. sin_addr);
            if ( send ( new_fd, registeredmsg , strlen(registeredmsg), 0) == - 1) { 
                perror ( "send error" ); 
            } 
            cout << "storageserver got a centerserver registered from " <<registeredcenter<<endl;
        }

        if(strcmp(registeredcenter,inet_ntoa(client_addr.sin_addr))){  
            cout << "you has not registered! please register first...." <<endl;
        }else{

            if(!xmlStrcmp(szAttr,BAD_CAST "startstorage")){  //匹配getrtspuri
                xmlNodePtr sNode = curNode->xmlChildrenNode;
                while (sNode != NULL){
                    if(!xmlStrcmp(sNode->name,BAD_CAST "profile")){
                        xmlNodePtr cNode = sNode->xmlChildrenNode;
                        cout << sNode->name <<endl;
                        while(cNode != NULL){
                        	if(!xmlStrcmp(cNode->name,BAD_CAST "deviceip")){
                            	deviceip = (char *)xmlNodeGetContent(cNode);
                                cout <<"\t"<< cNode->name << " : "<<xmlNodeGetContent(cNode) <<endl;
                            }
                            if (!xmlStrcmp(cNode->name,BAD_CAST "rtspuri")){
                            	rtspuri = (char *)xmlNodeGetContent(cNode);
                                cout <<"\t"<< cNode->name << " : "<<xmlNodeGetContent(cNode) <<endl;
                            }
                            if(!xmlStrcmp(cNode->name,BAD_CAST "height")){
                            	height = atoi((char *)xmlNodeGetContent(cNode));
                                cout <<"\t"<< cNode->name << " : "<<xmlNodeGetContent(cNode) <<endl;
                            }
                            if(!xmlStrcmp(cNode->name,BAD_CAST "width")){
                            	width = atoi((char *)xmlNodeGetContent(cNode));
                                cout <<"\t"<< cNode->name << " : "<<xmlNodeGetContent(cNode) <<endl;
                            }
                            if(!xmlStrcmp(cNode->name,BAD_CAST "split")){
                            	fileOutputIntervalset = atoi((char *)xmlNodeGetContent(cNode));
                                cout <<"\t"<< cNode->name << " : "<<xmlNodeGetContent(cNode) <<endl;
                            }
                            if(!xmlStrcmp(cNode->name,BAD_CAST "format")){
                            	filename_suffix = (char *)xmlNodeGetContent(cNode);
                                cout <<"\t"<< cNode->name << " : "<<xmlNodeGetContent(cNode) <<endl;
                            }
                            cNode = cNode->next;
                        }
                        openURL(*env, progName, rtspuri , deviceip);
                        xmlNewChild(sNode,NULL,(xmlChar *) "action",(xmlChar *) "success");
                        xmlFree(cNode);
                    }
                    sNode = sNode->next;
                }
                xmlFree(sNode);

                xmlChar *xml_buff;
                int size;
                xmlDocDumpMemory(doc,&xml_buff,&size);
                printf("%s-----%d\n",(char *)xml_buff,size);
                if (send ( new_fd, (char*)xml_buff , strlen((char*)xml_buff), 0) == - 1) { 
                    perror ( "send error" ); 
                } 
            }
            if(!xmlStrcmp(szAttr,BAD_CAST "stopstorage")){  //匹配getrtspuri
                xmlNodePtr sNode = curNode->xmlChildrenNode;
                while (sNode != NULL){
                    if(!xmlStrcmp(sNode->name,BAD_CAST "profile")){
                        xmlNodePtr cNode = sNode->xmlChildrenNode;
                        cout << sNode->name <<endl;
                        while(cNode != NULL){
                            if (!xmlStrcmp(cNode->name,BAD_CAST "rtspuri")){
                            	//shutdownStream(lookupClientByRTSPURL((char *)xmlNodeGetContent(cNode)),1);
                            	lookupClientByRTSPURL((char *)xmlNodeGetContent(cNode))->stopStorage();
                                cout <<"\t"<< cNode->name << " : "<<xmlNodeGetContent(cNode) <<endl;
                                xmlNewChild(sNode,NULL,(xmlChar *) "action",(xmlChar *) "success");
                            }
                            cNode = cNode->next;
                        }
                        xmlFree(cNode);
                    }
                    sNode = sNode->next;
                }
                xmlFree(sNode);
                xmlChar *xml_buff;
                int size;
                xmlDocDumpMemory(doc,&xml_buff,&size);
                printf("%s-----%d\n",(char *)xml_buff,size);
                if (send ( new_fd, (char*)xml_buff , strlen((char*)xml_buff), 0) == - 1) { 
                    perror ( "send error" ); 
                } 
            }

        }
        xmlFree(szAttr);
    }
    xmlFreeDoc(doc);
}


int MkDir(char *dir)  
{  
    DIR *mydir = NULL;  
    if((mydir= opendir(dir))==NULL)//判断目录   
    {  
    	int ret = mkdir(dir, MODE);//创建目录  
      	if (ret != 0)  
      	{  
          	return -1;  
      	}  
      	printf("%s created sucess!\n", dir);  
    }  
    else  
    {  
        printf("%s exist!\n", dir);  
    }  
    return 0;  
}

